/**
 * 
 */
package com.ms.game.invitefriend;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.StringUtils;

import com.ms.game.invitefriend.bin.InviteCodeType;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.utility.GameUtility;

/**
 * 產生邀請碼
 * <p>
 * 請參考 {@link #create()}
 * 
 * @author TW15010
 */
public class InviteCodeGen {
	private static long lastUpdateTime = 0L;

	private static AtomicInteger seq = new AtomicInteger();

	/** 扣掉排除字元，打亂順序後對應 27 進制數值的字元表 */
	public static final char keysMap27[] = { 'Q', 'V', 'W', 'A', 'U', 'M', 'F', 'C', 'J', 'T', '3', 'L', '7', 'R',
			'G', 'Y', 'S', '5', 'D', 'H', '4', '9', 'P', '6', 'X', 'K', 'N' };

	/** 打亂順序的完整字元表 0-9, A-Z */
	public static final char keyMap36[] = { 'S', 'T', 'V', '9', 'K', '4', 'F', 'R', 'L', 'X', 'J', '7', '2', 'U',
			'Q', 'W', '5', 'N', 'A', 'P', 'B', 'M', '8', '6', '0', 'I', '1', 'Z', 'D', 'C', '3', 'H', 'E', 'G',
			'O', 'Y' };

	/**
	 * 將指定的數值轉成 bit 再 or 到 container 指定的位置
	 * 
	 * @param container
	 *            把指定的數值 or 到這裡麵
	 * @param putValue
	 *            要被 or 進去的數值
	 * @param valueBitLen
	 *            從要被 or 進去的數值取多少個 but
	 * @param dstBit
	 *            or 到 container 裡的位置 (bit index)
	 * @return 傳回被 or 後的 container int
	 */
	public static int putBit(int container, int putValue, int valueBitLen, int dstBit) {
		// 產生符合指定位置和長度的 bit mask
		int mask = GameUtility.ipow(2, valueBitLen) - 1;
		mask = mask << dstBit;

		// 將 putValue 位移到指定 bit 位置
		putValue = putValue << dstBit;

		// 跟 mask & 只留下 mask 範圍內的
		putValue = putValue & mask;

		// 將 putValue 合併到 container
		container |= putValue;

		return container;
	}

	/**
	 * 把指定的 int 轉成二進位並加到指定的 bit array 後面
	 * 
	 * @param num
	 *            要加入的 int
	 * @param bitLen
	 *            int 數值要取多少 bit 出來
	 * @param bitArray
	 *            目的 bit array
	 */
	public static void addIntToBitArray(int num, int bitLen, ArrayList<Integer> bitArray) {
		if (bitArray == null) {
			return;
		}

		// 起始位置，陣列的末端
		int begIndex = bitArray.size();

		// 從第一個開始掃一遍 int 裡面指定長度的 bit
		for (int i = 0; i < bitLen; i++) {
			int value = 0;
			if ((num & 1) == 1) {
				value = 1;
			}

			// 將每一個 bit 插入陣列中
			bitArray.add(begIndex, value);
			num >>= 1;
		}
	}

	/**
	 * 將從數值轉出的字串取前面幾位，再對照 {@link #keysMap27} 轉成隨機字元，把結果加到回傳的 buffer 裡面
	 * 
	 * @param strOfValue
	 *            從數值轉出的字串
	 * @param pickLen
	 *            指定從字串尾端開始取幾位數
	 * @param radix
	 *            字串使用的數值進制
	 * @param rtnStringBuilder
	 *            回傳結果的字串 buffer
	 */
	public static void convertAndAppend(String strOfValue, int pickLen, int radix, StringBuilder rtnStringBuilder) {
		if (rtnStringBuilder == null) {
			return;
		}

		// 轉換字串尾端指定長度的字元到回傳 buffer
		for (int i = 0; i < pickLen; i++) {
			// 計算起始位置
			int pos = strOfValue.length() - pickLen + i;

			// 取出字元
			String curChar = strOfValue.substring(pos, pos + 1);

			// 依照傳入的進制將字元還原成 10 進制數值
			int idx = Integer.valueOf(curChar, radix);

			// 對照字元表取出對應的字元，並放入回傳 buffer
			rtnStringBuilder.append(keysMap27[idx]);
		}
	}

	/**
	 * 產生邀請碼
	 * 
	 * @return 邀請碼
	 */
	public static synchronized String create(InviteCodeType type) {
		switch (type) {
		case INVITE_CODE:
			/**
			 * 時間(秒)轉成 27 進制的字串取前面六位，這個數值範圍 (0-QQQQQQ) 循環一次約 12 年，應該是夠用了
			 * 亂數值取兩位數，避免一秒內產生好幾次邀請碼時就會出現重複的情況
			 */
			break;
		case ACTIVITY_INVITE_CODE:
			/**
			 * 時間(秒)轉成 27 進制的字串取前面七位，這個數值範圍 (0-QQQQQQQ)=>27^7 -1  循環一次約331年，應該是夠用了 
			 * 亂數值取三位數，避免一秒內產生好幾次邀請碼時就會出現重複的情況
			 */
			break;
		case ACTIVITY_SHARE_INVITE_CODE:
			/**
			 * 12碼 
			 * 
			 */
			break;
		}
		return create(type.getTimeLen(),type.getRandomLen());
	}
	
	private static synchronized String create(int timeLen, int randomLen){
		// 數值轉換進制
		int base = keysMap27.length;

		// 邀請碼是由 [時間值六位數 + 亂數值兩位數] TTTTTTRR 組成

		// 邀請碼字串 buffer
		StringBuilder sb = new StringBuilder();

		// 取得秒數字串
		long time = System.currentTimeMillis() / 1000;
		String timeStr = Long.toString(time, base);

		// 轉成隨機字元
		convertAndAppend(timeStr, timeLen, base, sb);

		// 亂數值每秒重置一次
		if (lastUpdateTime != time) {
			seq.set(Helper.randomInt());
			lastUpdateTime = time;
		}

		/**
		 * 每次需要產生邀請碼就會 +1，
		 * 如果一秒內超過 27*27 個人要產生邀請碼就會出現重複的情況
		 */
		int random = seq.incrementAndGet();
		String randomStr = Integer.toString(random, base);

		// 轉成字串後如果位數不足就自己補 0 進去
		if (randomStr.length() < randomLen) {
			randomStr = StringUtils.repeat("0", randomLen - randomStr.length()) + randomStr;
		}

		// 轉成隨機字元
		convertAndAppend(randomStr, randomLen, base, sb);

		return sb.toString();
	}
}
